package com.pokermind.operator;

import com.pokermind.common.log.PlainLog;
import com.pokermind.common.log.TimeCostLog;
import com.pokermind.model.po.PokerUser;
import com.pokermind.repository.dao.PokerUserDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 扑克用户业务操作类 处理用户相关的业务逻辑
 *
 * @author PokerMind Team
 */
@Service
@Transactional
public class PokerUserOperator {

    private static final Logger logger = LoggerFactory.getLogger(PokerUserOperator.class);

    @Autowired
    private PokerUserDao pokerUserDao;

    /**
     * 高性能缓存：playerId -> userId 映射
     *
     * 架构设计： - 使用ConcurrentHashMap保证线程安全 - 缓存频繁查询的playerId到userId的映射关系 - 提升createActionRecord等高频操作的性能 - 支持自动缓存管理和清理
     *
     * 缓存策略： - 写入时更新：创建用户时自动加入缓存 - 读取时填充：查询时如果缓存缺失则自动填充 - 定期清理：避免内存泄漏（后续可通过@Scheduled实现）
     */
    private final ConcurrentHashMap<String, Long> playerIdToUserIdCache = new ConcurrentHashMap<>();

    /**
     * 创建新用户
     *
     * @param playerId   前端玩家ID（如 mp_player_004）
     * @param playerName 前端玩家显示名称（如 MiddlePosition，可为空）
     * @return 创建的用户
     * @throws IllegalArgumentException 如果玩家ID已存在
     */
    public PokerUser createUser(String playerId, String playerName) {
        PlainLog plainLog = new TimeCostLog("op", "createUser").put("playerId", playerId).put("playerName", playerName);
        try {
            // 参数验证
            if (playerId == null || playerId.trim().isEmpty()) {
                throw new IllegalArgumentException("PlayerId cannot be null or empty");
            }
            // 检查玩家ID是否已存在
            if (pokerUserDao.existsByPlayerId(playerId)) {
                plainLog.put("playerIdExists", true);
                throw new IllegalArgumentException("PlayerId already exists: " + playerId);
            }
            // 创建用户
            PokerUser user = new PokerUser(playerId, playerName);
            PokerUser savedUser = pokerUserDao.save(user);
            // 自动填充缓存：新用户创建时立即加入映射缓存
            if (savedUser.getId() != null && savedUser.getPlayerId() != null) {
                playerIdToUserIdCache.put(savedUser.getPlayerId(), savedUser.getId());
            }
            plainLog.put("userId", savedUser.getId()).put("createTime", savedUser.getCreateTime())
                .put("op_rslt", "done");

            logger.info(plainLog.toString());
            return savedUser;
        } catch (IllegalArgumentException e) {
            logger.warn(plainLog.put("op_rslt", "param_error:" + e.getMessage()).toString());
            throw e;
        } catch (Exception e) {
            logger.error(plainLog.put("op_rslt", "ex:" + e.getMessage()).toString(), e);
            throw new RuntimeException("Failed to create user", e);
        }
    }
    
    /**
     * 根据前端玩家ID查找用户
     *
     * @param playerId 前端玩家ID（如 mp_player_004）
     * @return 用户对象，如果不存在返回empty
     */
    @Transactional(readOnly = true)
    public Optional<PokerUser> findUserByPlayerId(String playerId) {
        return pokerUserDao.findByPlayerId(playerId);
    }

    /**
     * 高性能的playerId到userId映射查询 - 专用于行动记录创建
     *
     * 架构优势： - 优先使用缓存，避免重复数据库查询 - 缓存缺失时自动填充，确保后续查询高效 - 专门为createActionRecord等高频场景优化 - 线程安全，支持并发访问
     *
     * @param playerId 前端玩家ID
     * @return 数据库用户ID，如果不存在返回null
     */
    @Transactional(readOnly = true)
    public Long getPlayerIdToUserIdMapping(String playerId) {
        if (playerId == null || playerId.trim().isEmpty()) {
            return null;
        }

        String normalizedPlayerId = playerId.trim();

        // 🚀 优先从缓存获取
        Long cachedUserId = playerIdToUserIdCache.get(normalizedPlayerId);
        if (cachedUserId != null) {
            return cachedUserId;
        }

        // 💾 缓存缺失，查询数据库并填充缓存
        Optional<PokerUser> user = pokerUserDao.findByPlayerId(normalizedPlayerId);
        if (user.isPresent()) {
            Long userId = user.get().getId();
            playerIdToUserIdCache.put(normalizedPlayerId, userId);
            return userId;
        } else {
            return null;
        }
    }

    /**
     * 清除指定playerId的缓存 - 用于重试机制
     *
     * @param playerId 前端玩家ID
     */
    public void clearPlayerIdCache(String playerId) {
        if (playerId != null) {
            String normalizedPlayerId = playerId.trim();
            playerIdToUserIdCache.remove(normalizedPlayerId);
        }
    }

    /**
     * 清除所有缓存 - 用于系统重置或内存管理
     */
    public void clearAllCache() {
        int size = playerIdToUserIdCache.size();
        playerIdToUserIdCache.clear();
        logger.info("清除所有缓存，共{}条记录", size);
    }

    /**
     * 根据ID查找用户
     *
     * @param userId 用户ID
     * @return 用户对象，如果不存在返回empty
     */
    @Transactional(readOnly = true)
    public Optional<PokerUser> findUserById(Long userId) {
        return pokerUserDao.findById(userId);
    }

    /**
     * 更新用户统计信息
     *
     * @param userId 用户ID
     * @param profit 盈利
     * @return 更新的行数
     */
    public int updateUserStats(Long userId, BigDecimal profit) {
        PlainLog plainLog = new TimeCostLog("op", "updateUserStats").put("userId", userId).put("profit", profit);

        try {
            // 参数验证
            if (userId == null) {
                throw new IllegalArgumentException("User ID cannot be null");
            }

            // 更新统计信息
            int updated = pokerUserDao.updateUserStats(userId, 1, profit);
            plainLog.put("updated", updated).put("op_rslt", "done");

            logger.info(plainLog.toString());
            return updated;

        } catch (IllegalArgumentException e) {
            logger.warn(plainLog.put("op_rslt", "param_error:" + e.getMessage()).toString());
            throw e;
        } catch (Exception e) {
            logger.error(plainLog.put("op_rslt", "ex:" + e.getMessage()).toString(), e);
            throw new RuntimeException("Failed to update user stats", e);
        }
    }

    /**
     * 获取盈利排行榜
     *
     * @param limit 返回数量
     * @return 用户列表
     */
    @Transactional(readOnly = true)
    public List<PokerUser> getProfitLeaderboard(int limit) {
        return pokerUserDao.findTopUsersByProfit(limit);
    }

    /**
     * 获取手数排行榜
     *
     * @param limit 返回数量
     * @return 用户列表
     */
    @Transactional(readOnly = true)
    public List<PokerUser> getHandsLeaderboard(int limit) {
        return pokerUserDao.findTopUsersByHands(limit);
    }

    /**
     * 获取活跃用户
     *
     * @param hours 活跃时间范围（小时）
     * @return 活跃用户列表
     */
    @Transactional(readOnly = true)
    public List<PokerUser> getActiveUsers(int hours) {
        Long since = System.currentTimeMillis() - (hours * 3600 * 1000L);
        return pokerUserDao.findActiveUsersSince(since);
    }

    /**
     * 用户认证（简化版本，仅验证玩家ID是否存在）
     *
     * @param playerId 前端玩家ID
     * @return 用户对象，如果用户不存在返回empty
     */
    @Transactional(readOnly = true)
    public Optional<PokerUser> authenticateUser(String playerId) {
        return pokerUserDao.findByPlayerId(playerId);
    }

    /**
     * 更新用户信息
     *
     * @param user 用户对象
     * @return 更新的行数
     */
    public int updateUser(PokerUser user) {
        user.setUpdateTime(System.currentTimeMillis());
        return pokerUserDao.update(user);
    }

    /**
     * 删除用户
     *
     * @param userId 用户ID
     * @return 删除的行数
     */
    public int deleteUser(Long userId) {
        return pokerUserDao.deleteById(userId);
    }

    /**
     * 获取用户总数
     *
     * @return 用户总数
     */
    @Transactional(readOnly = true)
    public long getUserCount() {
        return pokerUserDao.count();
    }

    /**
     * V4.7: 根据playerId获取或创建用户
     * 如果玩家不存在，自动创建一个临时玩家记录
     *
     * @param playerId 前端玩家ID
     * @return 用户ID
     */
    public Long getOrCreatePlayerByPlayerId(String playerId) {
        if (playerId == null || playerId.trim().isEmpty()) {
            throw new IllegalArgumentException("PlayerId cannot be null or empty");
        }

        try {
            // 检查用户是否已存在
            Optional<PokerUser> existingUser = findUserByPlayerId(playerId);
            if (existingUser.isPresent()) {
                Long userId = existingUser.get().getId();
                playerIdToUserIdCache.put(playerId, userId);
                return userId;
            }

            // 用户不存在，自动创建
            logger.info("自动创建临时玩家: playerId={}", playerId);
            PokerUser newUser = new PokerUser(playerId, playerId);  // 使用playerId作为playerName
            PokerUser savedUser = pokerUserDao.save(newUser);
            
            // 更新缓存
            if (savedUser.getId() != null) {
                playerIdToUserIdCache.put(playerId, savedUser.getId());
            }
            
            logger.info("临时玩家创建成功: playerId={}, userId={}", playerId, savedUser.getId());
            return savedUser.getId();

        } catch (Exception e) {
            logger.error("获取或创建玩家失败: playerId={}", playerId, e);
            throw new RuntimeException("Failed to get or create player: " + playerId, e);
        }
    }
}